package daomuxiaoshuo

import (
	"fmt"
	"github.com/PuerkitoBio/goquery"
	"net/http"
	"os"
	"save-kindle/entity"
	"save-kindle/log"
	"save-kindle/source"
	"strings"
	"time"
)

// DMZSSource 盗墓小说
type DMZSSource struct {
	source.BookInfo
}

func NewDMZSSource() *DMZSSource {
	return &DMZSSource{
		source.BookInfo{
			WebSite: "http://www.daomuxiaoshuo.net",
		},
	}
}

func (source *DMZSSource) NewBook(bookId, bookName string) {
	source.BookId, source.BookName = bookId, bookName
}

func (source *DMZSSource) GetArticle(bookId, bookName string) {
	source.NewBook(bookId, bookName)

	// 获取目录
	catalog, _ := source.GetCatalog()

	finishFlag := make(chan entity.Article, 10)
	err := os.Mkdir(bookName, 0777)
	if err != nil {
		log.PrintErr(err)
	}

	for i := range catalog {
		go func(index int, path string) {
			log.Println(index, catalog[index])
			article := catalog[index]
			article, _ = source.GetArticleContent(article)
			writeFile(index, path, article)
			finishFlag <- article
		}(i, bookName)
	}

	count := 0

	for len(catalog) != count {
		select {
		case finishArticle := <-finishFlag:
			log.Println("finish:", finishArticle.ArticleName)
			count++
		}
	}
}

// GetCatalog 获取文章目录
func (source *DMZSSource) GetCatalog() ([]entity.Article, error) {
	// http://www.daomuxiaoshuo.net/shahai/
	bookUrl := fmt.Sprintf("%s/%s/", source.WebSite, source.BookId)

	request, _ := http.NewRequest("GET", bookUrl, nil)

	response, err := http.DefaultClient.Do(request)
	if err != nil {
		log.PrintErr(err.Error())
		return nil, err
	}
	defer response.Body.Close()

	// 解析章节
	document, err := goquery.NewDocumentFromReader(response.Body)
	if err != nil {
		log.PrintErr(err.Error())
		return nil, err
	}

	var articles []entity.Article
	// class
	document.Find(".span3").Each(func(_ int, classSelection *goquery.Selection) {
		aTag := classSelection.Find("a")
		attr, exists := aTag.Attr("href")
		if exists {
			article := entity.Article{
				Index:       len(articles),
				ArticleUrl:  attr,
				ArticleName: aTag.Text(),
			}
			// log.Println(article)
			articles = append(articles, article)
		}

	})

	return articles, nil
}

// GetArticleContent 获取文章内容
func (source *DMZSSource) GetArticleContent(article entity.Article) (entity.Article, error) {
	text := ""
retry:
	request, _ := http.NewRequest("GET", fmt.Sprintf("%s%s", source.WebSite, article.ArticleUrl), nil)
	response, err := http.DefaultClient.Do(request)
	if err != nil {
		log.PrintErr(err.Error())
		return article, err
	}
	defer response.Body.Close()

	if response.StatusCode != http.StatusOK {
		time.Sleep(1 * time.Second)
		log.PrintErr("retry", article)
		goto retry
	}

	document, err := goquery.NewDocumentFromReader(response.Body)
	if err != nil {
		log.PrintErr(err.Error())
		return article, err
	}

	tempText := ""
	nextPage := ""

	document.Find(".span12").Each(func(i int, classSelection *goquery.Selection) {
		classSelection.Find("p").Each(func(i int, pTagSelection *goquery.Selection) {
			text += tempText
			tempText = pTagSelection.Text()
			tempText = strings.ReplaceAll(pTagSelection.Text(), "\t", "")
			//log.Println(pTagSelection.Text())
		})
		liTag := classSelection.Find(".pagination").Find("li")
		if liTag.Size() > 0 {
			attr := liTag.Get(liTag.Size() - 1).FirstChild.Attr
			for attrIndex := range attr {
				if attr[attrIndex].Key == "href" && attr[attrIndex].Val != "#" {
					nextPage = attr[attrIndex].Val
				}
			}
		}
	})

	if nextPage != "" {
		if strings.Contains(nextPage, source.BookId) {
			article.ArticleUrl = nextPage
		} else {
			article.ArticleUrl = article.ArticleUrl[:strings.LastIndex(article.ArticleUrl, "/")+1] + nextPage
		}
		goto retry
	}

	article.ArticleContent = text
	if article.ArticleContent == "" {
		// 暂停1s进行重试
		log.PrintErr("retry", article)
		time.Sleep(750 * time.Millisecond)
		goto retry
	}

	time.Sleep(750 * time.Millisecond)
	return article, nil
}

// writeFile 写入文件
func writeFile(index int, path string, v entity.Article) {
	file, err := os.OpenFile(fmt.Sprintf("%s/%d.txt", path, index), os.O_CREATE|os.O_APPEND|os.O_RDWR, 0666)
	if err != nil {
		log.PrintErr(err.Error())
	}
	defer file.Close()

	file.WriteString(v.ArticleName)
	file.WriteString(v.ArticleContent)
}
